package me.zd.tool.hardlink.service;

import me.zd.tool.hardlink.model.FullSetting;
import me.zd.tool.hardlink.model.LinkSetting;

import java.io.File;
import java.io.IOException;
import java.util.Properties;
import java.util.function.Consumer;
import java.util.function.Function;

public class ConfigService {
    public static final ConfigService INS = new ConfigService();
    private static final String DEFAULT_CONFIG_PATH = "config.properties";
    private static final String CHECK_KEY = "_check";
    private final Properties defaultProperties; // 默认配置

    private ConfigService() {
        Properties p = PropertiesService.loadFromResource("me/zd/tool/hardlink/config/default.properties");
        defaultProperties = (p != null) ? p : new Properties();
    }

    public void resetToDefault(FullSetting setting) {
        properties2Setting(defaultProperties, setting, false);
    }

    /**
     * 导入配置
     *
     * @param setting                配置model
     * @param path                   路径
     * @param failoverDefaultSetting 读取配置失败使用默认配置
     * @param monitor                监视器
     * @return 导入是否成功
     * @throws IOException 导入过程中出现的异常
     */
    public boolean importSetting(FullSetting setting, String path, boolean failoverDefaultSetting, Consumer<String> monitor) throws IOException {
        if (path == null) {
            path = DEFAULT_CONFIG_PATH;
        }
        File file = new File(path);
        if (!file.isFile()) {
            if (failoverDefaultSetting) {
                properties2Setting(defaultProperties, setting, true);
                return true;
            } else {
                monitor.accept(I18nService.INS.format("config.load.failed", path));
                return false;
            }
        }

        Properties p = PropertiesService.loadFromFile(file);
        if (p == null || !p.containsKey(CHECK_KEY)) {
            monitor.accept(I18nService.INS.format("config.load.failed", path));
            return false;
        }
        properties2Setting(p, setting, true);
        monitor.accept(I18nService.INS.format("config.load.success", path));
        return true;
    }

    /**
     * 导出配置
     *
     * @param setting 配置model
     * @param path    路径
     * @param monitor 监视器
     * @throws IOException 导出过程中出现的异常
     */
    public void exportSetting(FullSetting setting, String path, Consumer<String> monitor) throws IOException {
        if (path == null) {
            path = DEFAULT_CONFIG_PATH;
        }
        Properties p = setting2Properties(setting);
        PropertiesService.writeToFile(p, path);
        monitor.accept(I18nService.INS.format("config.save.success", path));
    }

    public void properties2Setting(Properties p, FullSetting setting, boolean useDefault) {
        stringFromProperties(p, "language", useDefault, setting.language::setLanguage);
        LinkSetting link = setting.link;
        stringFromProperties(p, "fromPath", useDefault, link::setFromPath);
        stringFromProperties(p, "toPath", useDefault, link::setToPath);
        stringFromProperties(p, "includeSuffix", useDefault, link::setIncludeSuffix);
        stringFromProperties(p, "excludeSuffix", useDefault, link::setExcludeSuffix);
        convertFromProperties(p, "scanDepth", useDefault, str -> {
            if (str.matches("\\d{1,2}")) {
                link.setScanDepth(Integer.parseInt(str));
                return true;
            } else {
                return false;
            }
        });
        convertFromProperties(p, "usingRegexp", useDefault, str -> {
            link.setUsingRegexp("1".equals(str));
            return true;
        });
        convertFromProperties(p, "usingRegexpReplacement", useDefault, str -> {
            link.setUsingRegexpReplacement("1".equals(str));
            return true;
        });
        convertFromProperties(p, "usingRegexpWithParentLevel", useDefault, str -> {
            link.setUsingRegexpWithParentLevel("1".equals(str));
            return true;
        });
        stringFromProperties(p, "fromPattern", useDefault, link::setFromPattern);
        stringFromProperties(p, "toPattern", useDefault, link::setToPattern);
        convertFromProperties(p, "regexpWithParentLevel", useDefault, str -> {
            if (str.matches("\\d{1,2}")) {
                link.setRegexpWithParentLevel(Integer.parseInt(str));
                return true;
            } else {
                return false;
            }
        });
        stringFromProperties(p, "targetFilePrefix", useDefault, link::setTargetFilePrefix);
        stringFromProperties(p, "beginModifiedTime", useDefault, link::setBeginModifiedTime);
        stringFromProperties(p, "endModifiedTime", useDefault, link::setEndModifiedTime);
        convertFromProperties(p, "usingModifiedTime", useDefault, str -> {
            link.setUsingModifiedTime("1".equals(str));
            return true;
        });
    }

    private void stringFromProperties(Properties p, String key, boolean useDefault, Consumer<String> setter) {
        String r = p.getProperty(key);
        if (r != null) {
            setter.accept(r);
        } else if (useDefault) {
            setter.accept(defaultProperties.getProperty(key));
        }
    }

    private void convertFromProperties(Properties p, String key, boolean useDefault, Function<String, Boolean> convert) {
        String r = p.getProperty(key);
        if (r != null) {
            if (convert.apply(r)) {
                return;
            }
        }
        if (useDefault) {
            r = defaultProperties.getProperty(key);
            if (r != null) {
                convert.apply(r);
            }
        }
    }

    private Properties setting2Properties(FullSetting setting) {
        Properties p = new Properties();
        p.setProperty(CHECK_KEY, I18nService.INS.get("config.check.value"));
        p.setProperty("language", setting.language.getLanguage());
        LinkSetting link = setting.link;
        p.setProperty("fromPath", link.getFromPath());
        p.setProperty("toPath", link.getToPath());
        p.setProperty("includeSuffix", link.getIncludeSuffix());
        p.setProperty("excludeSuffix", link.getExcludeSuffix());
        p.setProperty("scanDepth", link.getScanDepth() + "");
        p.setProperty("usingRegexp", link.isUsingRegexp() ? "1" : "0");
        p.setProperty("usingRegexpReplacement", link.isUsingRegexpReplacement() ? "1" : "0");
        p.setProperty("usingRegexpWithParentLevel", link.isUsingRegexpWithParentLevel() ? "1" : "0");
        p.setProperty("fromPattern", link.getFromPattern());
        p.setProperty("toPattern", link.getToPattern());
        p.setProperty("regexpWithParentLevel", link.getRegexpWithParentLevel() + "");
        p.setProperty("targetFilePrefix", link.getTargetFilePrefix());
        p.setProperty("usingModifiedTime", link.isUsingModifiedTime() ? "1" : "0");
        p.setProperty("beginModifiedTime", link.getBeginModifiedTime());
        p.setProperty("endModifiedTime", link.getEndModifiedTime());
        return p;
    }

}
